package com.core.cloudcommon.security;

import com.alibaba.fastjson.JSON;
import com.core.cloudcommon.CommonCustomException;
import com.core.cloudcommon.redis.service.CloudRedisTemplateService;
import com.core.cloudcommon.redis.vo.CloudRedisVO;
import com.core.cloudcommon.enums.CloudSystemLoginResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author =====Lqj=====
 * @className ConcurrentLoginManager
 * @description 并发登录控制管理器
 * @create 2024/12/16 11:30
 **/
@Slf4j
@Component
public class ConcurrentLoginManager {

    @Autowired
    private CloudRedisTemplateService cloudRedisTemplateService;

    // Redis Key前缀
    private static final String USER_TOKENS_KEY = "user_tokens:";
    private static final String TOKEN_USER_KEY = "token_user:";
    private static final String USER_DEVICES_KEY = "user_devices:";

    // 配置参数
    @Value("${login.concurrent.max-sessions:3}") // 默认最多3个并发会话
    private int maxConcurrentSessions;

    @Value("${login.concurrent.kick-out-previous:true}") // 默认踢掉之前的登录
    private boolean kickOutPrevious;

    /**
     * 登录设备信息
     */
    public static class LoginDevice {
        private String token;
        private String deviceId;
        private String deviceName;
        private String deviceType;
        private String loginIp;
        private Date loginTime;
        private Date lastActiveTime;

        // 构造函数
        public LoginDevice() {}

        public LoginDevice(String token, String deviceId, String deviceName, 
                          String deviceType, String loginIp) {
            this.token = token;
            this.deviceId = deviceId;
            this.deviceName = deviceName;
            this.deviceType = deviceType;
            this.loginIp = loginIp;
            this.loginTime = new Date();
            this.lastActiveTime = new Date();
        }

        // Getters and Setters
        public String getToken() { return token; }
        public void setToken(String token) { this.token = token; }
        
        public String getDeviceId() { return deviceId; }
        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
        
        public String getDeviceName() { return deviceName; }
        public void setDeviceName(String deviceName) { this.deviceName = deviceName; }
        
        public String getDeviceType() { return deviceType; }
        public void setDeviceType(String deviceType) { this.deviceType = deviceType; }
        
        public String getLoginIp() { return loginIp; }
        public void setLoginIp(String loginIp) { this.loginIp = loginIp; }
        
        public Date getLoginTime() { return loginTime; }
        public void setLoginTime(Date loginTime) { this.loginTime = loginTime; }
        
        public Date getLastActiveTime() { return lastActiveTime; }
        public void setLastActiveTime(Date lastActiveTime) { this.lastActiveTime = lastActiveTime; }
    }

    /**
     * 处理用户登录，管理并发会话
     * @param userId 用户ID
     * @param token 新的token
     * @param deviceId 设备ID
     * @param deviceName 设备名称
     * @param deviceType 设备类型
     * @param loginIp 登录IP
     * @param tokenExpireTime token过期时间（秒）
     */
    public void handleUserLogin(Long userId, String token, String deviceId, 
                               String deviceName, String deviceType, String loginIp, 
                               long tokenExpireTime) {
        String userTokensKey = USER_TOKENS_KEY + userId;
        String tokenUserKey = TOKEN_USER_KEY + token;
        String userDevicesKey = USER_DEVICES_KEY + userId;

        // 获取用户当前的所有token
        List<String> currentTokens = getCurrentUserTokens(userId);
        
        // 获取用户当前的设备信息
        List<LoginDevice> currentDevices = getCurrentUserDevices(userId);

        // 检查是否超出并发限制
        if (currentTokens.size() >= maxConcurrentSessions) {
            if (kickOutPrevious) {
                // 踢掉最早的登录
                kickOutOldestSession(userId, currentTokens, currentDevices);
            } else {
                // 拒绝新登录
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200025.getCode(), 
                    String.format("并发登录超出限制，最多允许%d个设备同时登录", maxConcurrentSessions));
            }
        }

        // 添加新的token和设备信息
        currentTokens.add(token);
        LoginDevice newDevice = new LoginDevice(token, deviceId, deviceName, deviceType, loginIp);
        currentDevices.add(newDevice);

        // 更新Redis
        cloudRedisTemplateService.set(new CloudRedisVO(userTokensKey, JSON.toJSONString(currentTokens), tokenExpireTime));
        cloudRedisTemplateService.set(new CloudRedisVO(tokenUserKey, userId.toString(), tokenExpireTime));
        cloudRedisTemplateService.set(new CloudRedisVO(userDevicesKey, JSON.toJSONString(currentDevices), tokenExpireTime));

        log.info("用户 {} 在设备 {} 上登录成功，当前活跃会话数: {}", userId, deviceName, currentTokens.size());
    }

    /**
     * 用户退出登录
     * @param userId 用户ID
     * @param token 要退出的token
     */
    public void handleUserLogout(Long userId, String token) {
        String userTokensKey = USER_TOKENS_KEY + userId;
        String tokenUserKey = TOKEN_USER_KEY + token;
        String userDevicesKey = USER_DEVICES_KEY + userId;

        // 获取当前tokens和设备
        List<String> currentTokens = getCurrentUserTokens(userId);
        List<LoginDevice> currentDevices = getCurrentUserDevices(userId);

        // 移除指定token和对应设备
        currentTokens.remove(token);
        currentDevices.removeIf(device -> token.equals(device.getToken()));

        // 删除token-user映射
        cloudRedisTemplateService.del(tokenUserKey);

        // 更新Redis
        if (currentTokens.isEmpty()) {
            // 如果没有活跃token了，删除所有相关记录
            cloudRedisTemplateService.del(userTokensKey);
            cloudRedisTemplateService.del(userDevicesKey);
        } else {
            // 更新剩余的tokens和设备
            Long expireTime = cloudRedisTemplateService.getExpire(new CloudRedisVO(userTokensKey));
            cloudRedisTemplateService.set(new CloudRedisVO(userTokensKey, JSON.toJSONString(currentTokens), expireTime));
            cloudRedisTemplateService.set(new CloudRedisVO(userDevicesKey, JSON.toJSONString(currentDevices), expireTime));
        }

        log.info("用户 {} 退出登录，剩余活跃会话数: {}", userId, currentTokens.size());
    }

    /**
     * 踢掉用户的所有其他会话
     * @param userId 用户ID
     * @param currentToken 当前保留的token
     */
    public void kickOutOtherSessions(Long userId, String currentToken) {
        List<String> currentTokens = getCurrentUserTokens(userId);
        
        for (String token : currentTokens) {
            if (!token.equals(currentToken)) {
                // 删除其他token
                cloudRedisTemplateService.del(TOKEN_USER_KEY + token);
            }
        }

        // 只保留当前token
        String userTokensKey = USER_TOKENS_KEY + userId;
        String userDevicesKey = USER_DEVICES_KEY + userId;
        
        List<String> newTokens = Arrays.asList(currentToken);
        List<LoginDevice> currentDevices = getCurrentUserDevices(userId);
        List<LoginDevice> newDevices = new ArrayList<>();
        
        // 只保留当前token对应的设备
        for (LoginDevice device : currentDevices) {
            if (currentToken.equals(device.getToken())) {
                newDevices.add(device);
                break;
            }
        }

        Long expireTime = cloudRedisTemplateService.getExpire(new CloudRedisVO(userTokensKey));
        cloudRedisTemplateService.set(new CloudRedisVO(userTokensKey, JSON.toJSONString(newTokens), expireTime));
        cloudRedisTemplateService.set(new CloudRedisVO(userDevicesKey, JSON.toJSONString(newDevices), expireTime));

        log.info("用户 {} 的其他会话已被踢出，当前只保留1个会话", userId);
    }

    /**
     * 获取用户当前的所有token
     */
    private List<String> getCurrentUserTokens(Long userId) {
        String userTokensKey = USER_TOKENS_KEY + userId;
        Object tokensObj = cloudRedisTemplateService.get(new CloudRedisVO(userTokensKey));
        
        if (tokensObj == null) {
            return new ArrayList<>();
        }
        
        try {
            return JSON.parseArray(tokensObj.toString(), String.class);
        } catch (Exception e) {
            log.error("解析用户tokens失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    /**
     * 获取用户当前的设备信息
     */
    private List<LoginDevice> getCurrentUserDevices(Long userId) {
        String userDevicesKey = USER_DEVICES_KEY + userId;
        Object devicesObj = cloudRedisTemplateService.get(new CloudRedisVO(userDevicesKey));
        
        if (devicesObj == null) {
            return new ArrayList<>();
        }
        
        try {
            return JSON.parseArray(devicesObj.toString(), LoginDevice.class);
        } catch (Exception e) {
            log.error("解析用户设备信息失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    /**
     * 踢掉最早的登录会话
     */
    private void kickOutOldestSession(Long userId, List<String> currentTokens, List<LoginDevice> currentDevices) {
        if (currentTokens.isEmpty()) {
            return;
        }

        // 找到最早的登录时间
        LoginDevice oldestDevice = currentDevices.stream()
            .min(Comparator.comparing(LoginDevice::getLoginTime))
            .orElse(null);

        if (oldestDevice != null) {
            String oldestToken = oldestDevice.getToken();
            
            // 删除最早的token
            cloudRedisTemplateService.del(TOKEN_USER_KEY + oldestToken);
            currentTokens.remove(oldestToken);
            currentDevices.remove(oldestDevice);
            
            log.info("用户 {} 的最早登录会话已被踢出，设备: {}", userId, oldestDevice.getDeviceName());
        }
    }

    /**
     * 检查token是否有效（未被踢出）
     * @param token token
     * @return true-有效，false-无效
     */
    public boolean isTokenValid(String token) {
        String tokenUserKey = TOKEN_USER_KEY + token;
        Object userIdObj = cloudRedisTemplateService.get(new CloudRedisVO(tokenUserKey));
        return userIdObj != null;
    }

    /**
     * 从token获取用户ID
     * @param token token
     * @return 用户ID，如果token无效返回null
     */
    public Long getUserIdFromToken(String token) {
        String tokenUserKey = TOKEN_USER_KEY + token;
        Object userIdObj = cloudRedisTemplateService.get(new CloudRedisVO(tokenUserKey));
        
        if (userIdObj == null) {
            return null;
        }
        
        try {
            return Long.valueOf(userIdObj.toString());
        } catch (Exception e) {
            log.error("从token获取用户ID失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取用户的所有登录设备
     * @param userId 用户ID
     * @return 设备列表
     */
    public List<LoginDevice> getUserDevices(Long userId) {
        return getCurrentUserDevices(userId);
    }

    /**
     * 更新设备活跃时间
     * @param token token
     */
    public void updateDeviceActiveTime(String token) {
        Long userId = getUserIdFromToken(token);
        if (userId == null) {
            return;
        }

        List<LoginDevice> devices = getCurrentUserDevices(userId);
        boolean updated = false;
        
        for (LoginDevice device : devices) {
            if (token.equals(device.getToken())) {
                device.setLastActiveTime(new Date());
                updated = true;
                break;
            }
        }

        if (updated) {
            String userDevicesKey = USER_DEVICES_KEY + userId;
            Long expireTime = cloudRedisTemplateService.getExpire(new CloudRedisVO(userDevicesKey));
            cloudRedisTemplateService.set(new CloudRedisVO(userDevicesKey, JSON.toJSONString(devices), expireTime));
        }
    }
}
